package com.demo.security.access.audit;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;

import com.demo.security.access.LogicAttribute;
import com.demo.security.access.audit.prepost.PostAuditAttribute;
import com.demo.security.access.audit.prepost.PostInvocationAuditAttribute;
import com.demo.security.access.audit.prepost.PreAuditAttribute;
import com.demo.security.access.audit.prepost.PreInvocationAuditAttribute;
import com.demo.security.access.method.AbstractMethodLogicMetadataSource;
import com.demo.security.annotation.audit.PostAudit;
import com.demo.security.annotation.audit.PreAudit;
/**
 * 注解方式的审计逻辑处理元数据资源
 * 处理前置注解和后置注解
 * 取得审计注解, 进行处理
 * 
 * @author ryuu.kk
 *
 */
public class AuditAnnotationLogicMetadataSource extends AbstractMethodLogicMetadataSource {

	@Override
	public Collection<LogicAttribute> getAttributes(Method method, Class<?> targetClass) {
		if (method.getDeclaringClass() == Object.class) {
            return Collections.emptyList();
        }

        logger.trace("Looking for audit annotations for method '" +
                method.getName() + "' on target class '" + targetClass + "'");
        
        PreAudit preAudit = findAnnotation(method, targetClass, PreAudit.class);
        PostAudit postAudit = findAnnotation(method, targetClass, PostAudit.class);
        if (preAudit == null && postAudit == null ) {
            // There is no meta-data so return
            logger.trace("No expression annotations found");
            return Collections.emptyList();
        }
        //Pre
        int preLevel = preAudit == null ? 1 : preAudit.level();
        String preCode = preAudit == null ? null : preAudit.code();
        String preInfo = preAudit == null ? null : preAudit.info();
        //Post
        int postLevel = postAudit == null ? 1 : postAudit.level();
        String postCode = postAudit == null ? null : postAudit.code();
        String postInfo = postAudit == null ? null : postAudit.info();
        String target = postAudit == null ? null : postAudit.auditTarget();
        
        ArrayList<LogicAttribute> attrs = new ArrayList<LogicAttribute>(2);
        PreAuditAttribute preAuditAttribute = createPreAuditAttribute(preLevel, preCode, preInfo);
        PostAuditAttribute postAuditAttribute = createPostAuditAttribute(postLevel, postCode, postInfo, target);
        if (preAuditAttribute != null) {
        	attrs.add(preAuditAttribute);
        }
        if (postAuditAttribute != null) {
        	attrs.add(postAuditAttribute);
        }
        attrs.trimToSize();
        return attrs;
	}

	@Override
	public Collection<LogicAttribute> getAllAttributes() {
		return null;
	}
	
	/**
     * See {@link org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource#getAttributes(Method, Class)}
     * for the logic of this method. The ordering here is slightly different in that we consider method-specific
     * annotations on an interface before class-level ones.
     */
    private <A  extends Annotation> A findAnnotation(Method method, Class<?> targetClass, Class<A> annotationClass) {
        // The method may be on an interface, but we need attributes from the target class.
        // If the target class is null, the method will be unchanged.
        Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
        A annotation = AnnotationUtils.findAnnotation(specificMethod, annotationClass);

        if (annotation != null) {
            logger.debug(annotation + " found on specific method: " + specificMethod);
            return annotation;
        }
        // Check the original (e.g. interface) method
        if (specificMethod != method) {
            annotation = AnnotationUtils.findAnnotation(method, annotationClass);

            if (annotation != null) {
                logger.debug(annotation + " found on: " + method);
                return annotation;
            }
        }

        // Check the class-level (note declaringClass, not targetClass, which may not actually implement the method)
        annotation = AnnotationUtils.findAnnotation(specificMethod.getDeclaringClass(), annotationClass);

        if (annotation != null) {
            logger.debug(annotation + " found on: " + specificMethod.getDeclaringClass().getName());
            return annotation;
        }

        return null;
    }
    
    /**
     * 创建前置审计属性
     * @param level 审计级别
     * @param code 审计Code
     * @param info 审计信息
     * @return 审计配置属性
     */
    protected PreAuditAttribute createPreAuditAttribute(int level, String code, String info) {
    	if (level < 1 && code == null) {
    		return null;
    	}
    	return new PreInvocationAuditAttribute(level, code, info);
    }
    
    /**
     * 创建后处理审计属性
     * @param level 审计级别
     * @param code 审计Code
     * @param info 审计信息
     * @return 审计配置属性
     */
    protected PostAuditAttribute createPostAuditAttribute(int level, String code, String info, String targetClass) {
    	if (level < 1 || code == null ) {
    		return null;
    	}
    	return new PostInvocationAuditAttribute(level, code, info, targetClass);
    }
}
